package game;

import gps.api.GPSProblem;
import gps.api.GPSRule;
import gps.api.GPSState;
import gps.exception.NotAppliableException;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import rules.MoveDown;
import rules.MoveLeft;
import rules.MoveRight;
import rules.MoveUp;

public class GridLockProblem implements GPSProblem {

	private GPSState initialState;
	private final int width = 6;
	private final int height = 6;
	private final Point exit = new Point(5, 2);
	private final PieceBlue blue1 = new PieceBlue(-1, new Point(1, 2), 2, false);
	private final PieceBlue blue2 = new PieceBlue(-1, new Point(1, 2), 2, false);
	private final PieceBlue blue3 = new PieceBlue(-1, new Point(0, 2), 2, false);
	private PieceBlue blue = blue3;
	private List<Piece> pieces = usePieces20();
	private List<GPSRule> rules;
	private int heuristicNumber;
	private Random rand;

	public GridLockProblem(int heuristicNumber, Random rand) {
		Board board = new Board(width, height, exit, blue, pieces);
		initializeRules(board);
		this.rand = rand;
		initialState = new GridLockState(board);
		this.heuristicNumber = heuristicNumber;
	}
	
	public GridLockProblem(int heuristicNumber, Board board, Random rand) {
		initializeRules(board);
		this.rand = rand;
		initialState = new GridLockState(board);
		this.heuristicNumber = heuristicNumber;
	}

	protected void initializeRules(Board board) {
		rules = new ArrayList<GPSRule>();
		// Cargo todas las piezas
		List<Piece> allPieces = new ArrayList<Piece>();
		allPieces.addAll(board.getPieces());
		allPieces.add(board.getBlue());

		for (Piece piece : allPieces) {
			if (piece.isVertical()) {
				for (int i = 1; i < height; i++) {
					rules.add(new MoveDown(piece, i));
					rules.add(new MoveUp(piece, i));
				}
			} else {
				for (int i = 1; i < width; i++) {
					rules.add(new MoveLeft(piece, i));
					rules.add(new MoveRight(piece, i));
				}
			}
		}

	}

	@Override
	public GPSState getInitState() {
		return initialState;
	}

	@Override
	public List<GPSRule> getRules() {
		Collections.shuffle(rules, rand);
		return rules;
	}

	@Override
	public Integer getHValue(GPSState state) {
		switch (heuristicNumber) {
		case 1:
			return firstHeuristic(state);
		case 2:
			return secondAdmisibleHeuristic(state);
		case 3:
			return thirdHeuristic(state);
		default:
			return 1;
		}
	}

	private int firstHeuristic(GPSState state) {
		return 5 - blue.getPosition().x;
	}

	private int secondAdmisibleHeuristic(GPSState state) {
		int count = 0;
		for (int i = blue.getPosition().x + blue.getSize(); i < width; i++) {
			if (((GridLockState) state).getBoard().getTiles()[i][blue
					.getPosition().y] != 0) {
				count++;
			}
		}
		return count;
	}

	private int thirdHeuristic(GPSState state) {
		int movements = rules.size();
		for (GPSRule rule : rules) {
			try {
				rule.evalRule(state);
			} catch (NotAppliableException e) {
				movements--;
			}
		}
		return rules.size() - movements;
	}

	private static List<Piece> usePieces1() {
		List<Piece> pieces1 = new ArrayList<Piece>();
		pieces1.add(new Piece(1, new Point(0, 0), 2, false));
		pieces1.add(new Piece(2, new Point(5, 0), 3, true));
		pieces1.add(new Piece(3, new Point(0, 1), 3, true));
		pieces1.add(new Piece(4, new Point(3, 1), 3, true));
		pieces1.add(new Piece(5, new Point(0, 4), 2, true));
		pieces1.add(new Piece(6, new Point(4, 4), 2, false));
		// pieces1.add(new Piece(7, new Point(2, 5), 3, false));
		return pieces1;
	}

	private static List<Piece> usePieces10() {
		List<Piece> pieces1 = new ArrayList<Piece>();
		pieces1.add(new Piece(1, new Point(0, 0), 2, false));
		pieces1.add(new Piece(2, new Point(2, 0), 2, true));
		pieces1.add(new Piece(3, new Point(4, 0), 2, false));
		pieces1.add(new Piece(4, new Point(0, 1), 2, false));
		pieces1.add(new Piece(5, new Point(4, 1), 2, false));
		pieces1.add(new Piece(6, new Point(0, 2), 3, true));
		pieces1.add(new Piece(7, new Point(5, 2), 3, true));
		pieces1.add(new Piece(8, new Point(1, 3), 3, false));
		pieces1.add(new Piece(9, new Point(3, 4), 2, true));
		pieces1.add(new Piece(10, new Point(0, 5), 2, false));
		pieces1.add(new Piece(11, new Point(4, 5), 2, false));
		return pieces1;
	}

	private static List<Piece> usePieces20() {
		List<Piece> pieces1 = new ArrayList<Piece>();
		pieces1.add(new Piece(1, new Point(0, 0), 2, true));
		pieces1.add(new Piece(2, new Point(3, 0), 3, false));
		pieces1.add(new Piece(3, new Point(1, 1), 2, false));
		pieces1.add(new Piece(4, new Point(3, 1), 2, true));
		pieces1.add(new Piece(5, new Point(2, 2), 2, true));
		pieces1.add(new Piece(6, new Point(5, 2), 3, true));
		pieces1.add(new Piece(7, new Point(2, 4), 2, true));
		pieces1.add(new Piece(8, new Point(3, 4), 2, false));
		pieces1.add(new Piece(9, new Point(3, 5), 3, false));
		return pieces1;
	}

}
